Final Project

Section 1: Objective

For this project, I will be showing the log2(fold-change) in differential gene expression for RNA-seq data in two subsets of colon cancer: adenocarcinoma and cystic, mucinous and serous neoplasms. The final deliverable will be the the Glimma Vignette. The input files for the Glimma vignette will be HTSeq-count data obtained from The Cancer Genome Atlas Program (TCGA). I will be turning in an HTML containing my R Notebook.

Section 2: Datasets

Loading data

Data is obtained from TCGA. I filtered for RNA-Seq experimental strategy, TXT data format and HTSeq-counts workflow type. HTSeq-counts is a tool that quantifies the aligned reads overlapping a gene’s exons. HTSeq data does not have a header, is tab-delimited, the first column is the Ensembl gene ID and the second column is the number of mapped reads of the gene. The counts will be used in differential gene expression analysis using edgeR as the method. To look at the differential gene expression, the counts will be normalized using the calcNormFactors in edgeR and only reads that unambigously map to one gene are used.

RAW: URL for cystic, mucinous, and serous neoplasms, I will choose 30:

RAW: URL for adenocarcinoma, I will choose 30:

Unit test for data

The first 15 genes listed in the first column are:

ENSG00000000003.13
ENSG00000000005.5
ENSG00000000419.11
ENSG00000000457.12
ENSG00000000460.15
ENSG00000000938.11
ENSG00000000971.14
ENSG00000001036.12
ENSG00000001084.9
ENSG00000001167.13
ENSG00000001460.16
ENSG00000001461.15
ENSG00000001497.15
ENSG00000001561.6
ENSG00000001617.10

The last 15 lines listed in the first column are:

ENSGR0000263980.4   
ENSGR0000264510.4   
ENSGR0000264819.4   
ENSGR0000265658.4   
ENSGR0000270726.4   
ENSGR0000275287.3   
ENSGR0000276543.3   
ENSGR0000277120.3   
ENSGR0000280767.1   
ENSGR0000281849.1   
__no_feature    
__ambiguous 
__too_low_aQual 
__not_aligned   
__alignment_not_unique  

Unit test for normalized data

Make a box plots of unnormalized and normalized data

par(mfrow=c(1,2))
lcpm <- cpm(x2, log=TRUE)
boxplot(lcpm, las=2, col=col, main="")
title(main="A. Example: Unnormalised data",ylab="Log-cpm")
x2 <- calcNormFactors(x2)  
x2$samples$norm.factors

Proposed Analysis

This project will compute and analyze the logarithmic ratio of differential gene expression of two subtypes of colon cancer. EdgeR will be used to import, organize, and normalize the data, Mus.musculus will be used for gene annotions, limma will be used to examine the gene expression anaylsis and make exploratory plots, Glimma will be used to make these plots interactive. RColorBrewer and gplots will be used to make heatmaps.

Flowchart

library(DiagrammeR)
grViz("digraph flowchart {
      # node definitions with substituted label text
      node [fontname = Helvetica, shape = rectangle]        
      tab1 [label = '@@1']
      tab2 [label = '@@2']
      tab3 [label = '@@3']
      tab4 [label = '@@4']
      tab5 [label = '@@5']
      tab6 [label = '@@6']
      tab7 [label = '@@7']
      tab8 [label = '@@8']
      tab9 [label = '@@9']
      tab10 [label = '@@10']

      # edge definitions with the node IDs
      tab1 -> tab2;
      tab2 -> tab3;
      tab3 -> tab4;
      tab4 -> tab8 -> tab5;
      tab4 -> tab5 -> tab6 -> tab7 -> tab9 -> tab10
      }

      [1]: 'Download necessary libraries'
      [2]: 'Load and read datasets'
      [3]: 'Join datasets'
      [4]: 'Unit test: Data was properly loaded?'
      [5]: 'Normalize data'
      [6]: 'Find Mean Varience Trend'
      [7]: 'Analyze DE genes'
      [8]: 'Troubleshoot and fix errors'
      [9]: 'Make Interactive MDS plot'
      [10]: 'Make HeatMap of log-CPM data'
      ")

NA

Loading libraries

library(limma)
library(Glimma)
library(edgeR)
library(Mus.musculus)
library(gplots)
library(RColorBrewer)

Proposed Timeline and Milestones

Week 1: Run the Glimma vignette. I will install the necessary packages in R and understand each step in the vignette.

Week 2: Load in the data (joins, creating datasets) and do a simple, 1 line unit test to look at the data. I will download 60 datasets (30 from each subtype) and join multiple datasets.

Week 3: Confirm that the data was loaded in correctly and analyze data using the Glimma vignette.

Week 4: Troubleshoot for additional errors and enhance the user interface.

User Interface

I anticipate having boxplots, heatmaps, and interactive multi-dimensional scaling (MDS) plots done in an R Notebook. I will submit an HTML page of my completed R Notebook.

MDS plot

library(limma)
lcpm <- cpm(x, log=TRUE)
par(mfrow=c(1,2))
col.group <- group
levels(col.group) <-  brewer.pal(nlevels(col.group), "Set1")
col.group <- as.character(col.group)
col.lane <- lane
levels(col.lane) <-  brewer.pal(nlevels(col.lane), "Set2")
col.lane <- as.character(col.lane)
plotMDS(lcpm, labels=group, col=col.group)
title(main="A. Sample groups")
plotMDS(lcpm, labels=lane, col=col.lane, dim=c(3,4))
title(main="B. Sequencing lanes")

Make MDS plot interactive

library(glimma)
glMDSPlot(lcpm, labels=paste(group, lane, sep="_"), 
          groups=x$samples[,c(2,5)], launch=FALSE)

HeatMap of log-CPM data

library(gplots)
basal.vs.lp.topgenes <- basal.vs.lp$ENTREZID[1:100]
i <- which(v$genes$ENTREZID %in% basal.vs.lp.topgenes)
mycol <- colorpanel(1000,"blue","white","red")
heatmap.2(lcpm[i,], scale="row",
   labRow=v$genes$SYMBOL[i], labCol=group, 
   col=mycol, trace="none", density.info="none", 
   margin=c(8,6), lhei=c(2,10), dendrogram="column")

Ashley E Noriega,

Nov 13, 2019

TRGN 510 Final Project: Milestone 1

A script for setting up the Glimma Vignette

{ if (!requireNamespace(“BiocManager”, quietly = TRUE)) install.packages(“BiocManager”) BiocManager::install(“limma”) library(limma)

if (!requireNamespace(“BiocManager”, quietly = TRUE)) install.packages(“BiocManager”) BiocManager::install(“Glimma”) library(Glimma)

if (!requireNamespace(“BiocManager”, quietly = TRUE)) install.packages(“BiocManager”)

BiocManager::install(“edgeR”) library(edgeR)

if (!requireNamespace(“BiocManager”, quietly = TRUE)) install.packages(“BiocManager”)

BiocManager::install(“Mus.musculus”) library(Mus.musculus)

library(R.utils)

if (!requireNamespace(“BiocManager”, quietly = TRUE)) install.packages(“BiocManager”)

BiocManager::install(“CAMERA”) library(CAMERA) }

Data Packaging

Reading in count data

library(R.utils)
url <- "https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE63310&format=file"
utils::download.file(url, destfile="GSE63310_RAW.tar", mode="wb") 
trying URL 'https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE63310&format=file'
Content type 'application/x-tar' length 1996800 bytes (1.9 MB)
==================================================
downloaded 1.9 MB
utils::untar("GSE63310_RAW.tar", exdir = ".")
files <- c("GSM1545535_10_6_5_11.txt", "GSM1545536_9_6_5_11.txt", "GSM1545538_purep53.txt",
  "GSM1545539_JMS8-2.txt", "GSM1545540_JMS8-3.txt", "GSM1545541_JMS8-4.txt",
  "GSM1545542_JMS8-5.txt", "GSM1545544_JMS9-P7c.txt", "GSM1545545_JMS9-P8c.txt")
for(i in paste(files, ".gz", sep=""))
  R.utils::gunzip(i, overwrite=TRUE)
files <- c("GSM1545535_10_6_5_11.txt", "GSM1545536_9_6_5_11.txt",
"GSM1545538_purep53.txt", "GSM1545539_JMS8-2.txt", 
   "GSM1545540_JMS8-3.txt", "GSM1545541_JMS8-4.txt", 
   "GSM1545542_JMS8-5.txt", "GSM1545544_JMS9-P7c.txt", 
   "GSM1545545_JMS9-P8c.txt")
read.delim(files[1], nrow=5)

Reading the 9 text files into R and combining into a matrix of counts

library(edgeR)
x <- readDGE(files, columns=c(1,3))
class(x)
[1] "DGEList"
attr(,"package")
[1] "edgeR"
dim(x)
[1] 27179     9

Organizing sample information

library(edgeR)
samplenames <- substring(colnames(x), 12, nchar(colnames(x)))
samplenames
[1] "10_6_5_11" "9_6_5_11"  "purep53"   "JMS8-2"    "JMS8-3"    "JMS8-4"   
[7] "JMS8-5"    "JMS9-P7c"  "JMS9-P8c" 
colnames(x) <- samplenames
group <- as.factor(c("LP", "ML", "Basal", "Basal", "ML", "LP", 
                     "Basal", "ML", "LP"))
x$samples$group <- group
lane <- as.factor(rep(c("L004","L006","L008"), c(3,4,2)))
x$samples$lane <- lane
x$samples

Organizing gene annotations

library(Mus.musculus)
geneid <- rownames(x)
genes <- select(Mus.musculus, keys=geneid, columns=c("SYMBOL", "TXCHROM"), 
                keytype="ENTREZID")
'select()' returned 1:many mapping between keys and columns
head(genes)

Resolve duplicate gene IDs

library(Mus.musculus)
genes <- genes[!duplicated(genes$ENTREZID),]

Package in a DGEList-object containing raw count data with associated sample information and gene annotations

library(Mus.musculus)
x$genes <- genes
x
An object of class "DGEList"
$samples

$counts
           Samples
Tags        10_6_5_11 9_6_5_11 purep53 JMS8-2 JMS8-3 JMS8-4 JMS8-5 JMS9-P7c
  497097            1        2     342    526      3      3    535        2
  100503874         0        0       5      6      0      0      5        0
  100038431         0        0       0      0      0      0      1        0
  19888             0        1       0      0     17      2      0        1
  20671             1        1      76     40     33     14     98       18
           Samples
Tags        JMS9-P8c
  497097           0
  100503874        0
  100038431        0
  19888            0
  20671            8
27174 more rows ...

$genes
27174 more rows ...

Data Pre-processing

Transformations from the raw-scale: convert raw counts to counts per million (CPM) and log2-counts per million (log-CPM)

library(edgeR)
cpm <- cpm(x)
lcpm <- cpm(x, log=TRUE)
L <- mean(x$samples$lib.size) * 1e-6
M <- median(x$samples$lib.size) * 1e-6
c(L, M)
[1] 45.47932 51.35680
summary(lcpm)
   10_6_5_11          9_6_5_11          purep53            JMS8-2            JMS8-3       
 Min.   :-4.5078   Min.   :-4.5078   Min.   :-4.5078   Min.   :-4.5078   Min.   :-4.5078  
 1st Qu.: 0.5918   1st Qu.: 0.7426   1st Qu.: 0.9268   1st Qu.: 0.9126   1st Qu.: 0.6324  
 Median : 3.7052   Median : 3.8006   Median : 3.8703   Median : 3.8243   Median : 3.7806  
 Mean   : 3.1170   Mean   : 3.1779   Mean   : 3.2851   Mean   : 3.2451   Mean   : 3.1864  
 3rd Qu.: 5.6883   3rd Qu.: 5.6785   3rd Qu.: 5.6689   3rd Qu.: 5.6206   3rd Qu.: 5.7115  
 Max.   :14.9245   Max.   :13.4598   Max.   :12.8926   Max.   :12.7869   Max.   :12.9347  
     JMS8-4            JMS8-5           JMS9-P7c          JMS9-P8c      
 Min.   :-4.5078   Min.   :-4.5078   Min.   :-4.5078   Min.   :-4.5078  
 1st Qu.: 0.7447   1st Qu.: 0.9692   1st Qu.: 0.7163   1st Qu.: 0.6985  
 Median : 3.6579   Median : 3.7583   Median : 3.7439   Median : 3.7095  
 Mean   : 3.1956   Mean   : 3.2889   Mean   : 3.1527   Mean   : 3.1660  
 3rd Qu.: 5.6854   3rd Qu.: 5.6623   3rd Qu.: 5.6276   3rd Qu.: 5.6514  
 Max.   :14.9698   Max.   :13.2008   Max.   :12.8225   Max.   :14.0339  

Remove lowly expressed genes

library(edgeR)
table(rowSums(x$counts==0)==9)

FALSE 
16624 

Filter genes while keeping as many genes as possible with worthwile counts

library(edgeR)
keep.exprs <- filterByExpr(x, group=group)
x <- x[keep.exprs,, keep.lib.sizes=FALSE]
dim(x)
[1] 16624     9

Plot the density of log-CPM values for raw and filtered data

lcpm.cutoff <- log2(10/M + 2/L)
library(RColorBrewer)
nsamples <- ncol(x)
col <- brewer.pal(nsamples, "Paired")
par(mfrow=c(1,2))
plot(density(lcpm[,1]), col=col[1], lwd=2, ylim=c(0,0.26), las=2, main="", xlab="")
title(main="A. Raw data", xlab="Log-cpm")
abline(v=lcpm.cutoff, lty=3)
for (i in 2:nsamples){
den <- density(lcpm[,i])
lines(den$x, den$y, col=col[i], lwd=2)
}
legend("topright", samplenames, text.col=col, bty="n")
lcpm <- cpm(x, log=TRUE)
plot(density(lcpm[,1]), col=col[1], lwd=2, ylim=c(0,0.26), las=2, main="", xlab="")
title(main="B. Filtered data", xlab="Log-cpm")
abline(v=lcpm.cutoff, lty=3)
for (i in 2:nsamples){
den <- density(lcpm[,i])
lines(den$x, den$y, col=col[i], lwd=2)
}
legend("topright", samplenames, text.col=col, bty="n")

Normalising gene expression distributions

library(edgeR)
x <- calcNormFactors(x, method = "TMM")
x$samples$norm.factors
[1] 0.8943956 1.0250186 1.0459005 1.0458455 1.0162707 0.9217132 0.9961959 1.0861026 0.9839203

Improve visualization by duplicating data then adjusting the counts

x2 <- x
x2$samples$norm.factors <- 1
x2$counts[,1] <- ceiling(x2$counts[,1]*0.05)
x2$counts[,2] <- x2$counts[,2]*5

Boxplot expression distribution of samples for normalised and unnormalised data

par(mfrow=c(1,2))
lcpm <- cpm(x2, log=TRUE)
boxplot(lcpm, las=2, col=col, main="")
title(main="A. Example: Unnormalised data",ylab="Log-cpm")
x2 <- calcNormFactors(x2)  
x2$samples$norm.factors
[1] 0.05770899 6.08287835 1.22023972 1.16478991 1.19661094 1.04659233 1.15048074 1.25431164 1.10901983
lcpm <- cpm(x2, log=TRUE)
boxplot(lcpm, las=2, col=col, main="")
title(main="B. Example: Normalised data",ylab="Log-cpm")

Unsupervised clustering of cells: make multi-dimensional scaling plot (MDS) to show simmilarities and dissimilarities between samples in an unsupervised manner

library(limma)
lcpm <- cpm(x, log=TRUE)
par(mfrow=c(1,2))
col.group <- group
levels(col.group) <-  brewer.pal(nlevels(col.group), "Set1")
col.group <- as.character(col.group)
col.lane <- lane
levels(col.lane) <-  brewer.pal(nlevels(col.lane), "Set2")
col.lane <- as.character(col.lane)
plotMDS(lcpm, labels=group, col=col.group)
title(main="A. Sample groups")
plotMDS(lcpm, labels=lane, col=col.lane, dim=c(3,4))
title(main="B. Sequencing lanes")

Make interactive using Glimma, html page will be generarted and opened in a browser if launch=TRUE

library(Glimma)
glMDSPlot(lcpm, labels=paste(group, lane, sep="_"), 
          groups=x$samples[,c(2,5)], launch=FALSE)

Differential Expression Analysis

Creating a design matrix

design <- model.matrix(~0+group+lane)
colnames(design) <- gsub("group", "", colnames(design))
design
  Basal LP ML laneL006 laneL008
1     0  1  0        0        0
2     0  0  1        0        0
3     1  0  0        0        0
4     1  0  0        1        0
5     0  0  1        1        0
6     0  1  0        1        0
7     1  0  0        1        0
8     0  0  1        0        1
9     0  1  0        0        1
attr(,"assign")
[1] 1 1 1 2 2
attr(,"contrasts")
attr(,"contrasts")$group
[1] "contr.treatment"

attr(,"contrasts")$lane
[1] "contr.treatment"

Contrasts for pairwise comparisons between cell populations

library(Glimma)
contr.matrix <- makeContrasts(
   BasalvsLP = Basal-LP, 
   BasalvsML = Basal - ML, 
   LPvsML = LP - ML, 
   levels = colnames(design))
contr.matrix
          Contrasts
Levels     BasalvsLP BasalvsML LPvsML
  Basal            1         1      0
  LP              -1         0      1
  ML               0        -1     -1
  laneL006         0         0      0
  laneL008         0         0      0

Remove heteroscedascity from count data

library(Glimma)
par(mfrow=c(1,2))
v <- voom(x, design, plot=TRUE)

v
An object of class "EList"
$genes
16619 more rows ...

$targets

$E
        Samples
Tags     10_6_5_11  9_6_5_11   purep53     JMS8-2    JMS8-3    JMS8-4    JMS8-5   JMS9-P7c  JMS9-P8c
  497097 -4.292165 -3.856488 2.5185849  3.2931366 -4.459730 -3.994060 3.2869677 -3.2103696 -5.295316
  20671  -4.292165 -4.593453 0.3560126 -0.4073032 -1.200995 -1.943434 0.8442767 -0.3228444 -1.207853
  27395   3.876089  4.413107 4.5170045  4.5617546  4.344401  3.786363 3.8990635  4.3396075  4.124644
  18777   4.708774  5.571872 5.3964008  5.1623650  5.649355  5.081611 5.0602470  5.7513694  5.142436
  21399   4.785541  4.754537 5.3703795  5.1220551  4.869586  4.943840 5.1384776  5.0308985  4.979644
16619 more rows ...

$weights
          [,1]      [,2]      [,3]      [,4]      [,5]      [,6]      [,7]      [,8]      [,9]
[1,]  1.079413  1.332986 19.826915 20.273253  1.993686  1.395853 20.494977  1.107780  1.079413
[2,]  1.170357  1.456380  4.804866  8.660025  3.612508  2.626870  8.760149  3.211473  2.541942
[3,] 20.219073 25.573792 30.434759 28.528310 31.352260 25.743247 28.722497 21.200072 16.657930
[4,] 26.947557 32.505933 33.583128 33.232125 34.231754 32.354158 33.334340 30.348630 24.259801
[5,] 26.610864 28.501638 33.645479 33.206374 33.573492 31.996623 33.308490 25.171513 23.573305
16619 more rows ...

$design
  Basal LP ML laneL006 laneL008
1     0  1  0        0        0
2     0  0  1        0        0
3     1  0  0        0        0
4     1  0  0        1        0
5     0  0  1        1        0
6     0  1  0        1        0
7     1  0  0        1        0
8     0  0  1        0        1
9     0  1  0        0        1
attr(,"assign")
[1] 1 1 1 2 2
attr(,"contrasts")
attr(,"contrasts")$group
[1] "contr.treatment"

attr(,"contrasts")$lane
[1] "contr.treatment"

Apply voom precision weights to data

vfit <- lmFit(v, design)
vfit <- contrasts.fit(vfit, contrasts=contr.matrix)
efit <- eBayes(vfit)
plotSA(efit, main="Final model: Mean-variance trend")

Examine the number of DE genes

summary(decideTests(efit))
       BasalvsLP BasalvsML LPvsML
Down        4648      4927   3135
NotSig      7113      7026  10972
Up          4863      4671   2517

Set a minimum log-fold change(log-FC) of 1

tfit <- treat(vfit, lfc=1)
dt <- decideTests(tfit)
summary(dt)
       BasalvsLP BasalvsML LPvsML
Down        1632      1777    224
NotSig     12976     12790  16210
Up          2016      2057    190

Extract genes that are DE in multiple comparisons

de.common <- which(dt[,1]!=0 & dt[,2]!=0)
length(de.common)
[1] 2784
head(tfit$genes$SYMBOL[de.common], n=20)
 [1] "Xkr4"          "Rgs20"         "Cpa6"          "A830018L16Rik" "Sulf1"         "Eya1"         
 [7] "Msc"           "Sbspon"        "Pi15"          "Crispld1"      "Kcnq5"         "Rims1"        
[13] "Khdrbs2"       "Ptpn18"        "Prss39"        "Arhgef4"       "Cnga3"         "2010300C02Rik"
[19] "Aff3"          "Npas2"        
vennDiagram(dt[,1:2], circle.col=c("turquoise", "salmon"))

Extract and write results for all 3 comparisons (basalvsLP, basalvsML, and LPvsML) to a single output file

write.fit(tfit, dt, file="results.txt")

Examining individual DE genes from top to bottom

basal.vs.lp <- topTreat(tfit, coef=1, n=Inf)
basal.vs.ml <- topTreat(tfit, coef=2, n=Inf)
head(basal.vs.lp)
head(basal.vs.ml)

Summarize results for genes using mean-difference plots that highlight differentially expressed genes

plotMD(tfit, column=1, status=dt[,1], main=colnames(tfit)[1], 
       xlim=c(-8,13))

Make interactive mean-difference plot. To open html page in a browser make launch=TRUE.

glMDPlot(tfit, coef=1, status=dt, main=colnames(tfit)[1],         side.main="ENTREZID", counts=lcpm, groups=group, launch=TRUE)

Make heatmap

library(gplots)

Attaching package: ‘gplots’

The following object is masked from ‘package:S4Vectors’:

    space

The following object is masked from ‘package:stats’:

    lowess
basal.vs.lp.topgenes <- basal.vs.lp$ENTREZID[1:100]
i <- which(v$genes$ENTREZID %in% basal.vs.lp.topgenes)
mycol <- colorpanel(1000,"blue","white","red")
heatmap.2(lcpm[i,], scale="row",
   labRow=v$genes$SYMBOL[i], labCol=group, 
   col=mycol, trace="none", density.info="none", 
   margin=c(8,6), lhei=c(2,10), dendrogram="column")
Error in plot.new() : figure margins too large

Gene set testing by applying the camera method on c2 gene signatures from the Broad Institute’s MSigDB c2 collection

load("/Users/ashleynoriega/Downloads/mouse_c2_v5p2.rdata")
idx <- ids2indices(Mm.c2,id=rownames(v))
cam.BasalvsLP <- camera(v,idx,design,contrast=contr.matrix[,1])
head(cam.BasalvsLP,5)
cam.BasalvsML <- camera(v,idx,design,contrast=contr.matrix[,2])
head(cam.BasalvsML,5)
cam.LPvsML <- camera(v,idx,design,contrast=contr.matrix[,3])
head(cam.LPvsML,5)
barcodeplot(efit$t[,3], index=idx$LIM_MAMMARY_LUMINAL_MATURE_UP, 
            index2=idx$LIM_MAMMARY_LUMINAL_MATURE_DN, main="LPvsML")

LS0tCnRpdGxlOiAiVFJHTiA1MTAgRmluYWwgUHJvamVjdDogQ29sb24gQ2FuY2VyIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIEZpbmFsIFByb2plY3QKCiMjIFNlY3Rpb24gMTogT2JqZWN0aXZlCkZvciB0aGlzIHByb2plY3QsIEkgd2lsbCBiZSBzaG93aW5nIHRoZSBsb2cyKGZvbGQtY2hhbmdlKSBpbiBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIGZvciBSTkEtc2VxIGRhdGEgaW4gdHdvIHN1YnNldHMgb2YgY29sb24gY2FuY2VyOiBhZGVub2NhcmNpbm9tYSBhbmQgY3lzdGljLCBtdWNpbm91cyBhbmQgc2Vyb3VzIG5lb3BsYXNtcy4gVGhlIGZpbmFsIGRlbGl2ZXJhYmxlIHdpbGwgYmUgdGhlIHRoZSBHbGltbWEgVmlnbmV0dGUuIFRoZSBpbnB1dCBmaWxlcyBmb3IgdGhlIEdsaW1tYSB2aWduZXR0ZSB3aWxsIGJlIEhUU2VxLWNvdW50IGRhdGEgb2J0YWluZWQgZnJvbSBUaGUgQ2FuY2VyIEdlbm9tZSBBdGxhcyBQcm9ncmFtIChUQ0dBKS4gSSB3aWxsIGJlIHR1cm5pbmcgaW4gYW4gSFRNTCBjb250YWluaW5nIG15IFIgTm90ZWJvb2suCgojIyBTZWN0aW9uIDI6IERhdGFzZXRzCgojIyMgTG9hZGluZyBkYXRhCkRhdGEgaXMgb2J0YWluZWQgZnJvbSBUQ0dBLiBJIGZpbHRlcmVkIGZvciBSTkEtU2VxIGV4cGVyaW1lbnRhbCBzdHJhdGVneSwgVFhUIGRhdGEgZm9ybWF0IGFuZCBIVFNlcS1jb3VudHMgd29ya2Zsb3cgdHlwZS4gSFRTZXEtY291bnRzIGlzIGEgdG9vbCB0aGF0IHF1YW50aWZpZXMgdGhlIGFsaWduZWQgcmVhZHMgb3ZlcmxhcHBpbmcgYSBnZW5lJ3MgZXhvbnMuIEhUU2VxIGRhdGEgZG9lcyBub3QgaGF2ZSBhIGhlYWRlciwgaXMgdGFiLWRlbGltaXRlZCwgdGhlIGZpcnN0IGNvbHVtbiBpcyB0aGUgRW5zZW1ibCBnZW5lIElEIGFuZCB0aGUgc2Vjb25kIGNvbHVtbiBpcyB0aGUgbnVtYmVyIG9mIG1hcHBlZCByZWFkcyBvZiB0aGUgZ2VuZS4gVGhlIGNvdW50cyB3aWxsIGJlIHVzZWQgaW4gZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBhbmFseXNpcyB1c2luZyBlZGdlUiBhcyB0aGUgbWV0aG9kLiBUbyBsb29rIGF0IHRoZSBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uLCB0aGUgY291bnRzIHdpbGwgYmUgbm9ybWFsaXplZCB1c2luZyB0aGUgY2FsY05vcm1GYWN0b3JzIGluIGVkZ2VSIGFuZCBvbmx5IHJlYWRzIHRoYXQgdW5hbWJpZ291c2x5IG1hcCB0byBvbmUgZ2VuZSBhcmUgdXNlZC4KClJBVzogW1VSTCBmb3IgY3lzdGljLCBtdWNpbm91cywgYW5kIHNlcm91cyBuZW9wbGFzbXMsIEkgd2lsbCBjaG9vc2UgMzA6XShodHRwczovL3BvcnRhbC5nZGMuY2FuY2VyLmdvdi9yZXBvc2l0b3J5P2ZpbHRlcnM9JTdCIm9wIiUzQSJhbmQiJTJDImNvbnRlbnQiJTNBJTVCJTdCImNvbnRlbnQiJTNBJTdCImZpZWxkIiUzQSJjYXNlcy5jYXNlX2lkIiUyQyJ2YWx1ZSIlM0ElNUIic2V0X2lkJTNBQVc0NU02QUtUdF9yTWJHZERha1QiJTVEJTdEJTJDIm9wIiUzQSJJTiIlN0QlMkMlN0Iib3AiJTNBImluIiUyQyJjb250ZW50IiUzQSU3QiJmaWVsZCIlM0EiY2FzZXMuZGlzZWFzZV90eXBlIiUyQyJ2YWx1ZSIlM0ElNUIiQ3lzdGljJTJDJTIwTXVjaW5vdXMlMjBhbmQlMjBTZXJvdXMlMjBOZW9wbGFzbXMiJTVEJTdEJTdEJTJDJTdCIm9wIiUzQSJpbiIlMkMiY29udGVudCIlM0ElN0IiZmllbGQiJTNBImZpbGVzLmFuYWx5c2lzLndvcmtmbG93X3R5cGUiJTJDInZhbHVlIiUzQSU1QiJIVFNlcSUyMC0lMjBDb3VudHMiJTVEJTdEJTdEJTJDJTdCIm9wIiUzQSJpbiIlMkMiY29udGVudCIlM0ElN0IiZmllbGQiJTNBImZpbGVzLmRhdGFfZm9ybWF0IiUyQyJ2YWx1ZSIlM0ElNUIiVFhUIiU1RCU3RCU3RCUyQyU3QiJvcCIlM0EiaW4iJTJDImNvbnRlbnQiJTNBJTdCImZpZWxkIiUzQSJmaWxlcy5leHBlcmltZW50YWxfc3RyYXRlZ3kiJTJDInZhbHVlIiUzQSU1QiJSTkEtU2VxIiU1RCU3RCU3RCU1RCU3RCZzZWFyY2hUYWJsZVRhYj1jYXNlcykKClJBVzogW1VSTCBmb3IgYWRlbm9jYXJjaW5vbWEsIEkgd2lsbCBjaG9vc2UgMzA6XShodHRwczovL3BvcnRhbC5nZGMuY2FuY2VyLmdvdi9yZXBvc2l0b3J5P2ZpbHRlcnM9JTdCIm9wIiUzQSJhbmQiJTJDImNvbnRlbnQiJTNBJTVCJTdCImNvbnRlbnQiJTNBJTdCImZpZWxkIiUzQSJjYXNlcy5jYXNlX2lkIiUyQyJ2YWx1ZSIlM0ElNUIic2V0X2lkJTNBQVc0NU02QUtUdF9yTWJHZERha1QiJTVEJTdEJTJDIm9wIiUzQSJJTiIlN0QlMkMlN0Iib3AiJTNBImluIiUyQyJjb250ZW50IiUzQSU3QiJmaWVsZCIlM0EiY2FzZXMuZGlzZWFzZV90eXBlIiUyQyJ2YWx1ZSIlM0ElNUIiQWRlbm9tYXMlMjBhbmQlMjBBZGVub2NhcmNpbm9tYXMiJTVEJTdEJTdEJTJDJTdCIm9wIiUzQSJpbiIlMkMiY29udGVudCIlM0ElN0IiZmllbGQiJTNBImZpbGVzLmFuYWx5c2lzLndvcmtmbG93X3R5cGUiJTJDInZhbHVlIiUzQSU1QiJIVFNlcSUyMC0lMjBDb3VudHMiJTVEJTdEJTdEJTJDJTdCIm9wIiUzQSJpbiIlMkMiY29udGVudCIlM0ElN0IiZmllbGQiJTNBImZpbGVzLmRhdGFfZm9ybWF0IiUyQyJ2YWx1ZSIlM0ElNUIiVFhUIiU1RCU3RCU3RCUyQyU3QiJvcCIlM0EiaW4iJTJDImNvbnRlbnQiJTNBJTdCImZpZWxkIiUzQSJmaWxlcy5leHBlcmltZW50YWxfc3RyYXRlZ3kiJTJDInZhbHVlIiUzQSU1QiJSTkEtU2VxIiU1RCU3RCU3RCU1RCU3RCZzZWFyY2hUYWJsZVRhYj1jYXNlcykKCiMjIyBVbml0IHRlc3QgZm9yIGRhdGEgClRoZSBmaXJzdCAxNSBnZW5lcyBsaXN0ZWQgaW4gdGhlIGZpcnN0IGNvbHVtbiBhcmU6IApgYGBgCkVOU0cwMDAwMDAwMDAwMy4xMwpFTlNHMDAwMDAwMDAwMDUuNQpFTlNHMDAwMDAwMDA0MTkuMTEKRU5TRzAwMDAwMDAwNDU3LjEyCkVOU0cwMDAwMDAwMDQ2MC4xNQpFTlNHMDAwMDAwMDA5MzguMTEKRU5TRzAwMDAwMDAwOTcxLjE0CkVOU0cwMDAwMDAwMTAzNi4xMgpFTlNHMDAwMDAwMDEwODQuOQpFTlNHMDAwMDAwMDExNjcuMTMKRU5TRzAwMDAwMDAxNDYwLjE2CkVOU0cwMDAwMDAwMTQ2MS4xNQpFTlNHMDAwMDAwMDE0OTcuMTUKRU5TRzAwMDAwMDAxNTYxLjYKRU5TRzAwMDAwMDAxNjE3LjEwCmBgYGAKVGhlIGxhc3QgMTUgbGluZXMgbGlzdGVkIGluIHRoZSBmaXJzdCBjb2x1bW4gYXJlOgpgYGBgCkVOU0dSMDAwMDI2Mzk4MC40CQpFTlNHUjAwMDAyNjQ1MTAuNAkKRU5TR1IwMDAwMjY0ODE5LjQJCkVOU0dSMDAwMDI2NTY1OC40CQpFTlNHUjAwMDAyNzA3MjYuNAkKRU5TR1IwMDAwMjc1Mjg3LjMJCkVOU0dSMDAwMDI3NjU0My4zCQpFTlNHUjAwMDAyNzcxMjAuMwkKRU5TR1IwMDAwMjgwNzY3LjEJCkVOU0dSMDAwMDI4MTg0OS4xCQpfX25vX2ZlYXR1cmUJCl9fYW1iaWd1b3VzCQpfX3Rvb19sb3dfYVF1YWwJCl9fbm90X2FsaWduZWQJCl9fYWxpZ25tZW50X25vdF91bmlxdWUJCmBgYGAKIyMjIFVuaXQgdGVzdCBmb3Igbm9ybWFsaXplZCBkYXRhCk1ha2UgYSBib3ggcGxvdHMgb2YgdW5ub3JtYWxpemVkIGFuZCBub3JtYWxpemVkIGRhdGEKYGBge3J9CnBhcihtZnJvdz1jKDEsMikpCmxjcG0gPC0gY3BtKHgyLCBsb2c9VFJVRSkKYm94cGxvdChsY3BtLCBsYXM9MiwgY29sPWNvbCwgbWFpbj0iIikKdGl0bGUobWFpbj0iQS4gRXhhbXBsZTogVW5ub3JtYWxpc2VkIGRhdGEiLHlsYWI9IkxvZy1jcG0iKQp4MiA8LSBjYWxjTm9ybUZhY3RvcnMoeDIpICAKeDIkc2FtcGxlcyRub3JtLmZhY3RvcnMKYGBgCgojIyBQcm9wb3NlZCBBbmFseXNpcwpUaGlzIHByb2plY3Qgd2lsbCBjb21wdXRlIGFuZCBhbmFseXplIHRoZSBsb2dhcml0aG1pYyByYXRpbyBvZiBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIG9mIHR3byBzdWJ0eXBlcyBvZiBjb2xvbiBjYW5jZXIuIEVkZ2VSIHdpbGwgYmUgdXNlZCB0byBpbXBvcnQsIG9yZ2FuaXplLCBhbmQgbm9ybWFsaXplIHRoZSBkYXRhLCAgTXVzLm11c2N1bHVzIHdpbGwgYmUgdXNlZCBmb3IgZ2VuZSBhbm5vdGlvbnMsIGxpbW1hIHdpbGwgYmUgdXNlZCB0byBleGFtaW5lIHRoZSBnZW5lIGV4cHJlc3Npb24gYW5heWxzaXMgYW5kIG1ha2UgZXhwbG9yYXRvcnkgcGxvdHMsIEdsaW1tYSB3aWxsIGJlIHVzZWQgdG8gbWFrZSB0aGVzZSBwbG90cyBpbnRlcmFjdGl2ZS4gUkNvbG9yQnJld2VyIGFuZCBncGxvdHMgd2lsbCBiZSB1c2VkIHRvIG1ha2UgaGVhdG1hcHMuCgojIyMgRmxvd2NoYXJ0CmBgYHtyfQpsaWJyYXJ5KERpYWdyYW1tZVIpCmdyVml6KCJkaWdyYXBoIGZsb3djaGFydCB7CiAgICAgICMgbm9kZSBkZWZpbml0aW9ucyB3aXRoIHN1YnN0aXR1dGVkIGxhYmVsIHRleHQKICAgICAgbm9kZSBbZm9udG5hbWUgPSBIZWx2ZXRpY2EsIHNoYXBlID0gcmVjdGFuZ2xlXSAgICAgICAgCiAgICAgIHRhYjEgW2xhYmVsID0gJ0BAMSddCiAgICAgIHRhYjIgW2xhYmVsID0gJ0BAMiddCiAgICAgIHRhYjMgW2xhYmVsID0gJ0BAMyddCiAgICAgIHRhYjQgW2xhYmVsID0gJ0BANCddCiAgICAgIHRhYjUgW2xhYmVsID0gJ0BANSddCiAgICAgIHRhYjYgW2xhYmVsID0gJ0BANiddCiAgICAgIHRhYjcgW2xhYmVsID0gJ0BANyddCiAgICAgIHRhYjggW2xhYmVsID0gJ0BAOCddCiAgICAgIHRhYjkgW2xhYmVsID0gJ0BAOSddCiAgICAgIHRhYjEwIFtsYWJlbCA9ICdAQDEwJ10KCiAgICAgICMgZWRnZSBkZWZpbml0aW9ucyB3aXRoIHRoZSBub2RlIElEcwogICAgICB0YWIxIC0+IHRhYjI7CiAgICAgIHRhYjIgLT4gdGFiMzsKICAgICAgdGFiMyAtPiB0YWI0OwogICAgICB0YWI0IC0+IHRhYjggLT4gdGFiNTsKICAgICAgdGFiNCAtPiB0YWI1IC0+IHRhYjYgLT4gdGFiNyAtPiB0YWI5IC0+IHRhYjEwCiAgICAgIH0KCiAgICAgIFsxXTogJ0Rvd25sb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMnCiAgICAgIFsyXTogJ0xvYWQgYW5kIHJlYWQgZGF0YXNldHMnCiAgICAgIFszXTogJ0pvaW4gZGF0YXNldHMnCiAgICAgIFs0XTogJ1VuaXQgdGVzdDogRGF0YSB3YXMgcHJvcGVybHkgbG9hZGVkPycKICAgICAgWzVdOiAnTm9ybWFsaXplIGRhdGEnCiAgICAgIFs2XTogJ0ZpbmQgTWVhbiBWYXJpZW5jZSBUcmVuZCcKICAgICAgWzddOiAnQW5hbHl6ZSBERSBnZW5lcycKICAgICAgWzhdOiAnVHJvdWJsZXNob290IGFuZCBmaXggZXJyb3JzJwogICAgICBbOV06ICdNYWtlIEludGVyYWN0aXZlIE1EUyBwbG90JwogICAgICBbMTBdOiAnTWFrZSBIZWF0TWFwIG9mIGxvZy1DUE0gZGF0YScKICAgICAgIikKCmBgYAoKCiMjIyBMb2FkaW5nIGxpYnJhcmllcwpgYGB7cn0KbGlicmFyeShsaW1tYSkKbGlicmFyeShHbGltbWEpCmxpYnJhcnkoZWRnZVIpCmxpYnJhcnkoTXVzLm11c2N1bHVzKQpsaWJyYXJ5KGdwbG90cykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKIyMgUHJvcG9zZWQgVGltZWxpbmUgYW5kIE1pbGVzdG9uZXMKV2VlayAxOiBSdW4gdGhlIEdsaW1tYSB2aWduZXR0ZS4gSSB3aWxsIGluc3RhbGwgdGhlIG5lY2Vzc2FyeSBwYWNrYWdlcyBpbiBSIGFuZCB1bmRlcnN0YW5kIGVhY2ggc3RlcCBpbiB0aGUgdmlnbmV0dGUuCgpXZWVrIDI6IExvYWQgaW4gdGhlIGRhdGEgKGpvaW5zLCBjcmVhdGluZyBkYXRhc2V0cykgYW5kIGRvIGEgc2ltcGxlLCAxIGxpbmUgdW5pdCB0ZXN0IHRvIGxvb2sgYXQgdGhlIGRhdGEuIEkgd2lsbCBkb3dubG9hZCA2MCBkYXRhc2V0cyAoMzAgZnJvbSBlYWNoIHN1YnR5cGUpIGFuZCBqb2luIG11bHRpcGxlIGRhdGFzZXRzLgoKV2VlayAzOiBDb25maXJtIHRoYXQgdGhlIGRhdGEgd2FzIGxvYWRlZCBpbiBjb3JyZWN0bHkgYW5kIGFuYWx5emUgZGF0YSB1c2luZyB0aGUgR2xpbW1hIHZpZ25ldHRlLgoKV2VlayA0OiBUcm91Ymxlc2hvb3QgZm9yIGFkZGl0aW9uYWwgZXJyb3JzIGFuZCBlbmhhbmNlIHRoZSB1c2VyIGludGVyZmFjZS4KCiMjIFVzZXIgSW50ZXJmYWNlCkkgYW50aWNpcGF0ZSBoYXZpbmcgYm94cGxvdHMsIGhlYXRtYXBzLCBhbmQgaW50ZXJhY3RpdmUgbXVsdGktZGltZW5zaW9uYWwgc2NhbGluZyAoTURTKSBwbG90cyBkb25lIGluIGFuIFIgTm90ZWJvb2suIEkgd2lsbCBzdWJtaXQgYW4gSFRNTCBwYWdlIG9mIG15IGNvbXBsZXRlZCBSIE5vdGVib29rLgoKIyMjIE1EUyBwbG90CmBgYHtyfQpsaWJyYXJ5KGxpbW1hKQpsY3BtIDwtIGNwbSh4LCBsb2c9VFJVRSkKcGFyKG1mcm93PWMoMSwyKSkKY29sLmdyb3VwIDwtIGdyb3VwCmxldmVscyhjb2wuZ3JvdXApIDwtICBicmV3ZXIucGFsKG5sZXZlbHMoY29sLmdyb3VwKSwgIlNldDEiKQpjb2wuZ3JvdXAgPC0gYXMuY2hhcmFjdGVyKGNvbC5ncm91cCkKY29sLmxhbmUgPC0gbGFuZQpsZXZlbHMoY29sLmxhbmUpIDwtICBicmV3ZXIucGFsKG5sZXZlbHMoY29sLmxhbmUpLCAiU2V0MiIpCmNvbC5sYW5lIDwtIGFzLmNoYXJhY3Rlcihjb2wubGFuZSkKcGxvdE1EUyhsY3BtLCBsYWJlbHM9Z3JvdXAsIGNvbD1jb2wuZ3JvdXApCnRpdGxlKG1haW49IkEuIFNhbXBsZSBncm91cHMiKQpwbG90TURTKGxjcG0sIGxhYmVscz1sYW5lLCBjb2w9Y29sLmxhbmUsIGRpbT1jKDMsNCkpCnRpdGxlKG1haW49IkIuIFNlcXVlbmNpbmcgbGFuZXMiKQpgYGAKCiMjIyBNYWtlIE1EUyBwbG90IGludGVyYWN0aXZlCmBgYHtyfQpsaWJyYXJ5KGdsaW1tYSkKZ2xNRFNQbG90KGxjcG0sIGxhYmVscz1wYXN0ZShncm91cCwgbGFuZSwgc2VwPSJfIiksIAogICAgICAgICAgZ3JvdXBzPXgkc2FtcGxlc1ssYygyLDUpXSwgbGF1bmNoPUZBTFNFKQpgYGAKCiMjIyBIZWF0TWFwIG9mIGxvZy1DUE0gZGF0YQpgYGB7cn0KbGlicmFyeShncGxvdHMpCmJhc2FsLnZzLmxwLnRvcGdlbmVzIDwtIGJhc2FsLnZzLmxwJEVOVFJFWklEWzE6MTAwXQppIDwtIHdoaWNoKHYkZ2VuZXMkRU5UUkVaSUQgJWluJSBiYXNhbC52cy5scC50b3BnZW5lcykKbXljb2wgPC0gY29sb3JwYW5lbCgxMDAwLCJibHVlIiwid2hpdGUiLCJyZWQiKQpoZWF0bWFwLjIobGNwbVtpLF0sIHNjYWxlPSJyb3ciLAogICBsYWJSb3c9diRnZW5lcyRTWU1CT0xbaV0sIGxhYkNvbD1ncm91cCwgCiAgIGNvbD1teWNvbCwgdHJhY2U9Im5vbmUiLCBkZW5zaXR5LmluZm89Im5vbmUiLCAKICAgbWFyZ2luPWMoOCw2KSwgbGhlaT1jKDIsMTApLCBkZW5kcm9ncmFtPSJjb2x1bW4iKQpgYGAKCgojIEFzaGxleSBFIE5vcmllZ2EsCiMgTm92IDEzLCAyMDE5CiMgVFJHTiA1MTAgRmluYWwgUHJvamVjdDogTWlsZXN0b25lIDEKIyBBIHNjcmlwdCBmb3Igc2V0dGluZyB1cCB0aGUgR2xpbW1hIFZpZ25ldHRlCnsKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKQmlvY01hbmFnZXI6Omluc3RhbGwoImxpbW1hIikKbGlicmFyeShsaW1tYSkKCmlmICghcmVxdWlyZU5hbWVzcGFjZSgiQmlvY01hbmFnZXIiLCBxdWlldGx5ID0gVFJVRSkpCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJHbGltbWEiKSAKbGlicmFyeShHbGltbWEpCgppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQogICAgCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJlZGdlUiIpCmxpYnJhcnkoZWRnZVIpCgppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQoKQmlvY01hbmFnZXI6Omluc3RhbGwoIk11cy5tdXNjdWx1cyIpCmxpYnJhcnkoTXVzLm11c2N1bHVzKQoKbGlicmFyeShSLnV0aWxzKQoKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJDQU1FUkEiKQpsaWJyYXJ5KENBTUVSQSkKfQoKIyBEYXRhIFBhY2thZ2luZwoKIyMgUmVhZGluZyBpbiBjb3VudCBkYXRhCmBgYHtyfQpsaWJyYXJ5KFIudXRpbHMpCnVybCA8LSAiaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8vZG93bmxvYWQvP2FjYz1HU0U2MzMxMCZmb3JtYXQ9ZmlsZSIKdXRpbHM6OmRvd25sb2FkLmZpbGUodXJsLCBkZXN0ZmlsZT0iR1NFNjMzMTBfUkFXLnRhciIsIG1vZGU9IndiIikgCnV0aWxzOjp1bnRhcigiR1NFNjMzMTBfUkFXLnRhciIsIGV4ZGlyID0gIi4iKQpmaWxlcyA8LSBjKCJHU00xNTQ1NTM1XzEwXzZfNV8xMS50eHQiLCAiR1NNMTU0NTUzNl85XzZfNV8xMS50eHQiLCAiR1NNMTU0NTUzOF9wdXJlcDUzLnR4dCIsCiAgIkdTTTE1NDU1MzlfSk1TOC0yLnR4dCIsICJHU00xNTQ1NTQwX0pNUzgtMy50eHQiLCAiR1NNMTU0NTU0MV9KTVM4LTQudHh0IiwKICAiR1NNMTU0NTU0Ml9KTVM4LTUudHh0IiwgIkdTTTE1NDU1NDRfSk1TOS1QN2MudHh0IiwgIkdTTTE1NDU1NDVfSk1TOS1QOGMudHh0IikKZm9yKGkgaW4gcGFzdGUoZmlsZXMsICIuZ3oiLCBzZXA9IiIpKQogIFIudXRpbHM6Omd1bnppcChpLCBvdmVyd3JpdGU9VFJVRSkKZmlsZXMgPC0gYygiR1NNMTU0NTUzNV8xMF82XzVfMTEudHh0IiwgIkdTTTE1NDU1MzZfOV82XzVfMTEudHh0IiwKIkdTTTE1NDU1MzhfcHVyZXA1My50eHQiLCAiR1NNMTU0NTUzOV9KTVM4LTIudHh0IiwgCiAgICJHU00xNTQ1NTQwX0pNUzgtMy50eHQiLCAiR1NNMTU0NTU0MV9KTVM4LTQudHh0IiwgCiAgICJHU00xNTQ1NTQyX0pNUzgtNS50eHQiLCAiR1NNMTU0NTU0NF9KTVM5LVA3Yy50eHQiLCAKICAgIkdTTTE1NDU1NDVfSk1TOS1QOGMudHh0IikKcmVhZC5kZWxpbShmaWxlc1sxXSwgbnJvdz01KQpgYGAKCiMjIFJlYWRpbmcgdGhlIDkgdGV4dCBmaWxlcyBpbnRvIFIgYW5kIGNvbWJpbmluZyBpbnRvIGEgbWF0cml4IG9mIGNvdW50cwpgYGB7cn0KbGlicmFyeShlZGdlUikKeCA8LSByZWFkREdFKGZpbGVzLCBjb2x1bW5zPWMoMSwzKSkKY2xhc3MoeCkKZGltKHgpCmBgYAoKIyMgT3JnYW5pemluZyBzYW1wbGUgaW5mb3JtYXRpb24KYGBge3J9CmxpYnJhcnkoZWRnZVIpCnNhbXBsZW5hbWVzIDwtIHN1YnN0cmluZyhjb2xuYW1lcyh4KSwgMTIsIG5jaGFyKGNvbG5hbWVzKHgpKSkKc2FtcGxlbmFtZXMKY29sbmFtZXMoeCkgPC0gc2FtcGxlbmFtZXMKZ3JvdXAgPC0gYXMuZmFjdG9yKGMoIkxQIiwgIk1MIiwgIkJhc2FsIiwgIkJhc2FsIiwgIk1MIiwgIkxQIiwgCiAgICAgICAgICAgICAgICAgICAgICJCYXNhbCIsICJNTCIsICJMUCIpKQp4JHNhbXBsZXMkZ3JvdXAgPC0gZ3JvdXAKbGFuZSA8LSBhcy5mYWN0b3IocmVwKGMoIkwwMDQiLCJMMDA2IiwiTDAwOCIpLCBjKDMsNCwyKSkpCngkc2FtcGxlcyRsYW5lIDwtIGxhbmUKeCRzYW1wbGVzCmBgYAoKIyMgT3JnYW5pemluZyBnZW5lIGFubm90YXRpb25zCmBgYHtyfQpsaWJyYXJ5KE11cy5tdXNjdWx1cykKZ2VuZWlkIDwtIHJvd25hbWVzKHgpCmdlbmVzIDwtIHNlbGVjdChNdXMubXVzY3VsdXMsIGtleXM9Z2VuZWlkLCBjb2x1bW5zPWMoIlNZTUJPTCIsICJUWENIUk9NIiksIAogICAgICAgICAgICAgICAga2V5dHlwZT0iRU5UUkVaSUQiKQpoZWFkKGdlbmVzKQpgYGAKCiMjIFJlc29sdmUgZHVwbGljYXRlIGdlbmUgSURzCmBgYHtyfQpsaWJyYXJ5KE11cy5tdXNjdWx1cykKZ2VuZXMgPC0gZ2VuZXNbIWR1cGxpY2F0ZWQoZ2VuZXMkRU5UUkVaSUQpLF0KYGBgCgojIyBQYWNrYWdlIGluIGEgREdFTGlzdC1vYmplY3QgY29udGFpbmluZyByYXcgY291bnQgZGF0YSB3aXRoIGFzc29jaWF0ZWQgc2FtcGxlIGluZm9ybWF0aW9uIGFuZCBnZW5lIGFubm90YXRpb25zCmBgYHtyfQpsaWJyYXJ5KE11cy5tdXNjdWx1cykKeCRnZW5lcyA8LSBnZW5lcwp4CmBgYAoKIyBEYXRhIFByZS1wcm9jZXNzaW5nCgojIyBUcmFuc2Zvcm1hdGlvbnMgZnJvbSB0aGUgcmF3LXNjYWxlOiBjb252ZXJ0IHJhdyBjb3VudHMgdG8gY291bnRzIHBlciBtaWxsaW9uIChDUE0pIGFuZCBsb2cyLWNvdW50cyBwZXIgbWlsbGlvbiAobG9nLUNQTSkKYGBge3J9CmxpYnJhcnkoZWRnZVIpCmNwbSA8LSBjcG0oeCkKbGNwbSA8LSBjcG0oeCwgbG9nPVRSVUUpCkwgPC0gbWVhbih4JHNhbXBsZXMkbGliLnNpemUpICogMWUtNgpNIDwtIG1lZGlhbih4JHNhbXBsZXMkbGliLnNpemUpICogMWUtNgpjKEwsIE0pCnN1bW1hcnkobGNwbSkKYGBgCgojIyBSZW1vdmUgbG93bHkgZXhwcmVzc2VkIGdlbmVzCmBgYHtyfQpsaWJyYXJ5KGVkZ2VSKQp0YWJsZShyb3dTdW1zKHgkY291bnRzPT0wKT09OSkKYGBgCgojIyBGaWx0ZXIgZ2VuZXMgd2hpbGUga2VlcGluZyBhcyBtYW55IGdlbmVzIGFzIHBvc3NpYmxlIHdpdGggd29ydGh3aWxlIGNvdW50cwpgYGB7cn0KbGlicmFyeShlZGdlUikKa2VlcC5leHBycyA8LSBmaWx0ZXJCeUV4cHIoeCwgZ3JvdXA9Z3JvdXApCnggPC0geFtrZWVwLmV4cHJzLCwga2VlcC5saWIuc2l6ZXM9RkFMU0VdCmRpbSh4KQpgYGAKCiMjIFBsb3QgdGhlIGRlbnNpdHkgb2YgbG9nLUNQTSB2YWx1ZXMgZm9yIHJhdyBhbmQgZmlsdGVyZWQgZGF0YQpgYGB7cn0KbGNwbS5jdXRvZmYgPC0gbG9nMigxMC9NICsgMi9MKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbnNhbXBsZXMgPC0gbmNvbCh4KQpjb2wgPC0gYnJld2VyLnBhbChuc2FtcGxlcywgIlBhaXJlZCIpCnBhcihtZnJvdz1jKDEsMikpCnBsb3QoZGVuc2l0eShsY3BtWywxXSksIGNvbD1jb2xbMV0sIGx3ZD0yLCB5bGltPWMoMCwwLjI2KSwgbGFzPTIsIG1haW49IiIsIHhsYWI9IiIpCnRpdGxlKG1haW49IkEuIFJhdyBkYXRhIiwgeGxhYj0iTG9nLWNwbSIpCmFibGluZSh2PWxjcG0uY3V0b2ZmLCBsdHk9MykKZm9yIChpIGluIDI6bnNhbXBsZXMpewpkZW4gPC0gZGVuc2l0eShsY3BtWyxpXSkKbGluZXMoZGVuJHgsIGRlbiR5LCBjb2w9Y29sW2ldLCBsd2Q9MikKfQpsZWdlbmQoInRvcHJpZ2h0Iiwgc2FtcGxlbmFtZXMsIHRleHQuY29sPWNvbCwgYnR5PSJuIikKbGNwbSA8LSBjcG0oeCwgbG9nPVRSVUUpCnBsb3QoZGVuc2l0eShsY3BtWywxXSksIGNvbD1jb2xbMV0sIGx3ZD0yLCB5bGltPWMoMCwwLjI2KSwgbGFzPTIsIG1haW49IiIsIHhsYWI9IiIpCnRpdGxlKG1haW49IkIuIEZpbHRlcmVkIGRhdGEiLCB4bGFiPSJMb2ctY3BtIikKYWJsaW5lKHY9bGNwbS5jdXRvZmYsIGx0eT0zKQpmb3IgKGkgaW4gMjpuc2FtcGxlcyl7CmRlbiA8LSBkZW5zaXR5KGxjcG1bLGldKQpsaW5lcyhkZW4keCwgZGVuJHksIGNvbD1jb2xbaV0sIGx3ZD0yKQp9CmxlZ2VuZCgidG9wcmlnaHQiLCBzYW1wbGVuYW1lcywgdGV4dC5jb2w9Y29sLCBidHk9Im4iKQpgYGAKCiMjIE5vcm1hbGlzaW5nIGdlbmUgZXhwcmVzc2lvbiBkaXN0cmlidXRpb25zCmBgYHtyfQpsaWJyYXJ5KGVkZ2VSKQp4IDwtIGNhbGNOb3JtRmFjdG9ycyh4LCBtZXRob2QgPSAiVE1NIikKeCRzYW1wbGVzJG5vcm0uZmFjdG9ycwpgYGAKCiMjIEltcHJvdmUgdmlzdWFsaXphdGlvbiBieSBkdXBsaWNhdGluZyBkYXRhIHRoZW4gYWRqdXN0aW5nIHRoZSBjb3VudHMKYGBge3J9CngyIDwtIHgKeDIkc2FtcGxlcyRub3JtLmZhY3RvcnMgPC0gMQp4MiRjb3VudHNbLDFdIDwtIGNlaWxpbmcoeDIkY291bnRzWywxXSowLjA1KQp4MiRjb3VudHNbLDJdIDwtIHgyJGNvdW50c1ssMl0qNQpgYGAKCiMjIEJveHBsb3QgZXhwcmVzc2lvbiBkaXN0cmlidXRpb24gb2Ygc2FtcGxlcyBmb3Igbm9ybWFsaXNlZCBhbmQgdW5ub3JtYWxpc2VkIGRhdGEKYGBge3J9CnBhcihtZnJvdz1jKDEsMikpCmxjcG0gPC0gY3BtKHgyLCBsb2c9VFJVRSkKYm94cGxvdChsY3BtLCBsYXM9MiwgY29sPWNvbCwgbWFpbj0iIikKdGl0bGUobWFpbj0iQS4gRXhhbXBsZTogVW5ub3JtYWxpc2VkIGRhdGEiLHlsYWI9IkxvZy1jcG0iKQp4MiA8LSBjYWxjTm9ybUZhY3RvcnMoeDIpICAKeDIkc2FtcGxlcyRub3JtLmZhY3RvcnMKbGNwbSA8LSBjcG0oeDIsIGxvZz1UUlVFKQpib3hwbG90KGxjcG0sIGxhcz0yLCBjb2w9Y29sLCBtYWluPSIiKQp0aXRsZShtYWluPSJCLiBFeGFtcGxlOiBOb3JtYWxpc2VkIGRhdGEiLHlsYWI9IkxvZy1jcG0iKQpgYGAKCiMjIFVuc3VwZXJ2aXNlZCBjbHVzdGVyaW5nIG9mIGNlbGxzOiBtYWtlIG11bHRpLWRpbWVuc2lvbmFsIHNjYWxpbmcgcGxvdCAoTURTKSB0byBzaG93IHNpbW1pbGFyaXRpZXMgYW5kIGRpc3NpbWlsYXJpdGllcyBiZXR3ZWVuIHNhbXBsZXMgaW4gYW4gdW5zdXBlcnZpc2VkIG1hbm5lcgpgYGB7cn0KbGlicmFyeShsaW1tYSkKbGNwbSA8LSBjcG0oeCwgbG9nPVRSVUUpCnBhcihtZnJvdz1jKDEsMikpCmNvbC5ncm91cCA8LSBncm91cApsZXZlbHMoY29sLmdyb3VwKSA8LSAgYnJld2VyLnBhbChubGV2ZWxzKGNvbC5ncm91cCksICJTZXQxIikKY29sLmdyb3VwIDwtIGFzLmNoYXJhY3Rlcihjb2wuZ3JvdXApCmNvbC5sYW5lIDwtIGxhbmUKbGV2ZWxzKGNvbC5sYW5lKSA8LSAgYnJld2VyLnBhbChubGV2ZWxzKGNvbC5sYW5lKSwgIlNldDIiKQpjb2wubGFuZSA8LSBhcy5jaGFyYWN0ZXIoY29sLmxhbmUpCnBsb3RNRFMobGNwbSwgbGFiZWxzPWdyb3VwLCBjb2w9Y29sLmdyb3VwKQp0aXRsZShtYWluPSJBLiBTYW1wbGUgZ3JvdXBzIikKcGxvdE1EUyhsY3BtLCBsYWJlbHM9bGFuZSwgY29sPWNvbC5sYW5lLCBkaW09YygzLDQpKQp0aXRsZShtYWluPSJCLiBTZXF1ZW5jaW5nIGxhbmVzIikKYGBgCgojIyBNYWtlIGludGVyYWN0aXZlIHVzaW5nIEdsaW1tYSwgaHRtbCBwYWdlIHdpbGwgYmUgZ2VuZXJhcnRlZCBhbmQgb3BlbmVkIGluIGEgYnJvd3NlciBpZiBsYXVuY2g9VFJVRQpgYGB7cn0KbGlicmFyeShHbGltbWEpCmdsTURTUGxvdChsY3BtLCBsYWJlbHM9cGFzdGUoZ3JvdXAsIGxhbmUsIHNlcD0iXyIpLCAKICAgICAgICAgIGdyb3Vwcz14JHNhbXBsZXNbLGMoMiw1KV0sIGxhdW5jaD1GQUxTRSkKCmBgYAoKIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBBbmFseXNpcwoKIyMgQ3JlYXRpbmcgYSBkZXNpZ24gbWF0cml4CmBgYHtyfQpkZXNpZ24gPC0gbW9kZWwubWF0cml4KH4wK2dyb3VwK2xhbmUpCmNvbG5hbWVzKGRlc2lnbikgPC0gZ3N1YigiZ3JvdXAiLCAiIiwgY29sbmFtZXMoZGVzaWduKSkKZGVzaWduCmBgYAoKIyMgQ29udHJhc3RzIGZvciBwYWlyd2lzZSBjb21wYXJpc29ucyBiZXR3ZWVuIGNlbGwgcG9wdWxhdGlvbnMKYGBge3J9CmxpYnJhcnkoR2xpbW1hKQpjb250ci5tYXRyaXggPC0gbWFrZUNvbnRyYXN0cygKICAgQmFzYWx2c0xQID0gQmFzYWwtTFAsIAogICBCYXNhbHZzTUwgPSBCYXNhbCAtIE1MLCAKICAgTFB2c01MID0gTFAgLSBNTCwgCiAgIGxldmVscyA9IGNvbG5hbWVzKGRlc2lnbikpCmNvbnRyLm1hdHJpeApgYGAKCiMjIFJlbW92ZSBoZXRlcm9zY2VkYXNjaXR5IGZyb20gY291bnQgZGF0YQpgYGB7cn0KbGlicmFyeShHbGltbWEpCnBhcihtZnJvdz1jKDEsMikpCnYgPC0gdm9vbSh4LCBkZXNpZ24sIHBsb3Q9VFJVRSkKdgpgYGAKCiMjIEFwcGx5IHZvb20gcHJlY2lzaW9uIHdlaWdodHMgdG8gZGF0YQpgYGB7cn0KdmZpdCA8LSBsbUZpdCh2LCBkZXNpZ24pCnZmaXQgPC0gY29udHJhc3RzLmZpdCh2Zml0LCBjb250cmFzdHM9Y29udHIubWF0cml4KQplZml0IDwtIGVCYXllcyh2Zml0KQpwbG90U0EoZWZpdCwgbWFpbj0iRmluYWwgbW9kZWw6IE1lYW4tdmFyaWFuY2UgdHJlbmQiKQpgYGAKCgojIyBFeGFtaW5lIHRoZSBudW1iZXIgb2YgREUgZ2VuZXMKYGBge3J9CnN1bW1hcnkoZGVjaWRlVGVzdHMoZWZpdCkpCmBgYAoKIyMgU2V0IGEgbWluaW11bSBsb2ctZm9sZCBjaGFuZ2UobG9nLUZDKSBvZiAxCmBgYHtyfQp0Zml0IDwtIHRyZWF0KHZmaXQsIGxmYz0xKQpkdCA8LSBkZWNpZGVUZXN0cyh0Zml0KQpzdW1tYXJ5KGR0KQpgYGAKCiMjIEV4dHJhY3QgZ2VuZXMgdGhhdCBhcmUgREUgaW4gbXVsdGlwbGUgY29tcGFyaXNvbnMKYGBge3J9CmRlLmNvbW1vbiA8LSB3aGljaChkdFssMV0hPTAgJiBkdFssMl0hPTApCmxlbmd0aChkZS5jb21tb24pCmhlYWQodGZpdCRnZW5lcyRTWU1CT0xbZGUuY29tbW9uXSwgbj0yMCkKdmVubkRpYWdyYW0oZHRbLDE6Ml0sIGNpcmNsZS5jb2w9YygidHVycXVvaXNlIiwgInNhbG1vbiIpKQpgYGAKCiMjIEV4dHJhY3QgYW5kIHdyaXRlIHJlc3VsdHMgZm9yIGFsbCAzIGNvbXBhcmlzb25zIChiYXNhbHZzTFAsIGJhc2FsdnNNTCwgYW5kIExQdnNNTCkgdG8gYSBzaW5nbGUgb3V0cHV0IGZpbGUKYGBge3J9CndyaXRlLmZpdCh0Zml0LCBkdCwgZmlsZT0icmVzdWx0cy50eHQiKQpgYGAKCiMjIEV4YW1pbmluZyBpbmRpdmlkdWFsIERFIGdlbmVzIGZyb20gdG9wIHRvIGJvdHRvbQpgYGB7cn0KYmFzYWwudnMubHAgPC0gdG9wVHJlYXQodGZpdCwgY29lZj0xLCBuPUluZikKYmFzYWwudnMubWwgPC0gdG9wVHJlYXQodGZpdCwgY29lZj0yLCBuPUluZikKaGVhZChiYXNhbC52cy5scCkKYGBgCgpgYGB7cn0KaGVhZChiYXNhbC52cy5tbCkKYGBgCgojIyBTdW1tYXJpemUgcmVzdWx0cyBmb3IgZ2VuZXMgdXNpbmcgbWVhbi1kaWZmZXJlbmNlIHBsb3RzIHRoYXQgaGlnaGxpZ2h0IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcwpgYGB7cn0KcGxvdE1EKHRmaXQsIGNvbHVtbj0xLCBzdGF0dXM9ZHRbLDFdLCBtYWluPWNvbG5hbWVzKHRmaXQpWzFdLCAKICAgICAgIHhsaW09YygtOCwxMykpCmBgYAoKIyMgTWFrZSBpbnRlcmFjdGl2ZSBtZWFuLWRpZmZlcmVuY2UgcGxvdC4gVG8gb3BlbiBodG1sIHBhZ2UgaW4gYSBicm93c2VyIG1ha2UgbGF1bmNoPVRSVUUuCmBgYHtyfQpnbE1EUGxvdCh0Zml0LCBjb2VmPTEsIHN0YXR1cz1kdCwgbWFpbj1jb2xuYW1lcyh0Zml0KVsxXSwgICAgICAgICBzaWRlLm1haW49IkVOVFJFWklEIiwgY291bnRzPWxjcG0sIGdyb3Vwcz1ncm91cCwgbGF1bmNoPVRSVUUpCmBgYAoKIyMgTWFrZSBoZWF0bWFwCmBgYHtyfQpsaWJyYXJ5KGdwbG90cykKYmFzYWwudnMubHAudG9wZ2VuZXMgPC0gYmFzYWwudnMubHAkRU5UUkVaSURbMToxMDBdCmkgPC0gd2hpY2godiRnZW5lcyRFTlRSRVpJRCAlaW4lIGJhc2FsLnZzLmxwLnRvcGdlbmVzKQpteWNvbCA8LSBjb2xvcnBhbmVsKDEwMDAsImJsdWUiLCJ3aGl0ZSIsInJlZCIpCmhlYXRtYXAuMihsY3BtW2ksXSwgc2NhbGU9InJvdyIsCiAgIGxhYlJvdz12JGdlbmVzJFNZTUJPTFtpXSwgbGFiQ29sPWdyb3VwLCAKICAgY29sPW15Y29sLCB0cmFjZT0ibm9uZSIsIGRlbnNpdHkuaW5mbz0ibm9uZSIsIAogICBtYXJnaW49Yyg4LDYpLCBsaGVpPWMoMiwxMCksIGRlbmRyb2dyYW09ImNvbHVtbiIpCmBgYAoKIyBHZW5lIHNldCB0ZXN0aW5nIGJ5IGFwcGx5aW5nIHRoZSBjYW1lcmEgbWV0aG9kIG9uIGMyIGdlbmUgc2lnbmF0dXJlcyBmcm9tIHRoZSBCcm9hZCBJbnN0aXR1dGXigJlzIE1TaWdEQiBjMiBjb2xsZWN0aW9uCmBgYHtyfQpsb2FkKCIvVXNlcnMvYXNobGV5bm9yaWVnYS9Eb3dubG9hZHMvbW91c2VfYzJfdjVwMi5yZGF0YSIpCmlkeCA8LSBpZHMyaW5kaWNlcyhNbS5jMixpZD1yb3duYW1lcyh2KSkKY2FtLkJhc2FsdnNMUCA8LSBjYW1lcmEodixpZHgsZGVzaWduLGNvbnRyYXN0PWNvbnRyLm1hdHJpeFssMV0pCmhlYWQoY2FtLkJhc2FsdnNMUCw1KQpjYW0uQmFzYWx2c01MIDwtIGNhbWVyYSh2LGlkeCxkZXNpZ24sY29udHJhc3Q9Y29udHIubWF0cml4WywyXSkKaGVhZChjYW0uQmFzYWx2c01MLDUpCmNhbS5MUHZzTUwgPC0gY2FtZXJhKHYsaWR4LGRlc2lnbixjb250cmFzdD1jb250ci5tYXRyaXhbLDNdKQpoZWFkKGNhbS5MUHZzTUwsNSkKYGBgCgoKYGBge3J9CmJhcmNvZGVwbG90KGVmaXQkdFssM10sIGluZGV4PWlkeCRMSU1fTUFNTUFSWV9MVU1JTkFMX01BVFVSRV9VUCwgCiAgICAgICAgICAgIGluZGV4Mj1pZHgkTElNX01BTU1BUllfTFVNSU5BTF9NQVRVUkVfRE4sIG1haW49IkxQdnNNTCIpCmBgYAoK